<?php
/**
 * RouteCity 工具类
 *
 * 基于高德地图 Web API，按“起终点 + 各 step 首坐标”采样策略，
 * 获取两城市间沿途经过的市级行政区列表。
 *
 * 使用示例：
 *   require 'extend/RouteCity.php';
 *   $cities = RouteCity::between('常州', '深圳', 'YOUR_GAODE_KEY');
 */
class RouteCity
{
    const API_KEY = '70f41f1bc6e86c70d21cd2730dd99235';
    /**
     * 核心对外方法：获取沿途城市列表
     * @param string $startCity 起点城市
     * @param string $endCity 终点城市
     * @return array 沿途城市列表
     */
    public static function between(string $startCity, string $endCity ): array
    {
        $origin      = self::geocode($startCity, self::API_KEY);
        $destination = self::geocode($endCity, self::API_KEY);

        $steps = self::direction($origin, $destination, self::API_KEY);

        // 采样：起终点 + 每个 step 的首坐标
        $samplePoints = [$origin, $destination];
        foreach ($steps as $step) {
            $points = explode(';', $step['polyline']);
            if ($points) {
                $samplePoints[] = $points[0];
            }
        }
        $samplePoints = array_values(array_unique($samplePoints));

        // 批量逆地理获取城市
        $cities = self::batchReverseGeocode($samplePoints, self::API_KEY);
        array_unshift($cities, $startCity);
        if (end($cities) !== $endCity) {
            $cities[] = $endCity;
        }

        // 按规范化名称去重，保留原始名称顺序
        $unique = [];
        $seen   = [];
        foreach ($cities as $c) {
            $norm = self::normalizeCity($c);
            if (!in_array($norm, $seen, true)) {
                $seen[]   = $norm;
                $unique[] = $c;
            }
        }
        return $unique;
    }

    /* ===== 内部辅助 ===== */

    private static function request(string $url): string
    {
        $ch = curl_init($url);
        curl_setopt_array($ch, [
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_SSL_VERIFYPEER => false,
            CURLOPT_CONNECTTIMEOUT => 5,
            CURLOPT_TIMEOUT        => 10,
        ]);
        $resp = curl_exec($ch);
        if (curl_errno($ch)) {
            throw new RuntimeException('Curl error: ' . curl_error($ch));
        }
        curl_close($ch);
        return $resp;
    }

    private static function geocode(string $address, string $key): string
    {
        $url  = sprintf(
            'https://restapi.amap.com/v3/geocode/geo?address=%s&city=&output=JSON&key=%s',
            urlencode($address),
            $key
        );
        $data = json_decode(self::request($url), true);
        if (($data['status'] ?? '0') === '1' && !empty($data['geocodes'][0]['location'])) {
            return $data['geocodes'][0]['location'];
        }
        throw new RuntimeException('Geocode failed: ' . ($data['info'] ?? 'unknown'));
    }

    private static function direction(string $origin, string $destination, string $key): array
    {
        $url  = sprintf(
            'https://restapi.amap.com/v3/direction/driving?origin=%s&destination=%s&extensions=base&output=JSON&key=%s',
            $origin,
            $destination,
            $key
        );
        $data = json_decode(self::request($url), true);
        if (($data['status'] ?? '0') === '1' && !empty($data['route']['paths'][0]['steps'])) {
            return $data['route']['paths'][0]['steps'];
        }
        throw new RuntimeException('Direction failed: ' . ($data['info'] ?? 'unknown'));
    }

    private static function batchReverseGeocode(array $locations, string $key): array
    {
        $chunks = array_chunk($locations, 20);
        $cities = [];
        foreach ($chunks as $chunk) {
            $locStr = implode('|', $chunk);
            $url    = sprintf(
                'https://restapi.amap.com/v3/geocode/regeo?location=%s&batch=1&output=JSON&key=%s',
                $locStr,
                $key
            );
            $data = json_decode(self::request($url), true);
            if (($data['status'] ?? '0') !== '1') {
                throw new RuntimeException('ReGeo failed: ' . ($data['info'] ?? 'unknown'));
            }
            $regeocodes = $data['regeocodes'] ?? (isset($data['regeocode']) ? [$data['regeocode']] : []);
            if (empty($regeocodes)) {
                continue; // 可能位于海域或返回为空
            }
            foreach ($regeocodes as $item) {
                $ac = $item['addressComponent'];
                $city = $ac['city'] ?: $ac['province'];
                $city = is_array($city) ? ($city[0] ?? '') : $city;
                if ($city !== '') {
                    $cities[] = $city;
                }
            }
        }
        return array_values(array_unique($cities, SORT_STRING));
    }

    // 城市名称归一化：去除常见后缀
    private static function normalizeCity(string $name): string
    {
        return preg_replace('/(市|地区|盟|自治州)$/u', '', $name);
    }
}